package com.kakame.codenews.base.web.fast;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.CharUtils;
import org.apache.commons.lang3.StringUtils;

import com.kakame.codenews.base.util.DateUtil;

/**
 * 实体生成器
 * 
 * @author 何家明 2018年6月30日 下午3:44:27
 */
public class EntityGenerator {
	private Connection connection;
	private Statement statement;
	
	private static final String ENTITY_PATH = "com.kakame.codenews.base.web.fast.mapping";  // 生成java文件的位置
	
	private static final String COLUMN_NAME = "columnName";
	private static final String DATA_TYPE = "dataType";
	private static final String COLUMN_COMMENT = "columnComment";
	private static final String TABLE_COMMENT = "tableComment";
	
	private static final String DB_DRIVER = "com.mysql.jdbc.Driver";
	private static final String DB_URL = "jdbc:mysql://39.108.136.4:3306/codenews";
	private static final String DB_USER = "liberty";
	private static final String DB_PASSWORD = "liberty";
	
	private static final String AUTHOR_SIGN = "@author 何家明  " + DateUtil.formatDate(DateUtil.getNow(), DateUtil.FORMAT1);

	public static void main(String[] args) throws Exception {
		EntityGenerator instance = new EntityGenerator();
		instance.initConnection(DB_DRIVER, DB_URL, DB_USER, DB_PASSWORD);
		
		Map<String, List<Map<String, String>>> resultMap = instance.readAllTableAndColumns();
		Set<String> tables = resultMap.keySet();  // 所有的表
		for (String tableName : tables) {
			generateEntity(tableName, resultMap.get(tableName));
		}
		
		instance.closeConnect();
	}

	/**
	 * 生成java实体
	 * @author 何家明 2018年6月30日 下午7:05:35
	 * @param tableName
	 * @param list
	 */
	private static void generateEntity(String tableName, List<Map<String, String>> list) {
		String[] tableNames = tableName.split("_");
		String tableLevel = "";
		String tempEntityName = "";
		if(tableNames.length > 2) {
			String tableFlag = tableNames[0];  // 表标识
			tableLevel = tableNames[1];  // 表分类
			tempEntityName = tableName.replace(tableFlag + "_" + tableLevel + "_", "");
		} else {
			tempEntityName = tableName;
		}
		
		String classFileName = toJavaClassName(tempEntityName);  // 将要生成的类的名字
		PrintWriter pw = null;
		try {
			String canonicalPath = new File("").getCanonicalPath();  // E:\Develop\workspace\git\codenews
			String entityPath = ENTITY_PATH.replace(".", "\\");
			String fileFullPath = "";
			if(StringUtils.isNotBlank(tableLevel)) {
				fileFullPath = canonicalPath + "\\src\\main\\java\\" + entityPath + "\\" + tableLevel;
			} else {
				fileFullPath = canonicalPath + "\\src\\main\\java\\" + entityPath;
			}
			File file = new File(fileFullPath);
			if(!file.exists()) {
				file.mkdirs();
			}
			
			pw = new PrintWriter(new File(fileFullPath + "\\" + classFileName + ".java"));
			StringBuilder content = new StringBuilder();
			// 包名
			if(StringUtils.isNotBlank(tableLevel)) {
				content.append("package " + ENTITY_PATH + "." + tableLevel + ";\n\n");
			} else {
				content.append("package " + ENTITY_PATH + ";\n\n");
			}
			// 导入
			content.append("import java.io.Serializable;\n");
			String importDate = "";
			String importDecimal = "";
			for (Map<String, String> map : list) {
				String dataType = map.get(DATA_TYPE);
				if("varchar".equals(dataType) || "text".equals(dataType)) {
					
				} else if("int".equals(dataType)) {
					
				} else if("datetime".equals(dataType) || "date".equals(dataType)) {
					importDate = "import java.util.Date;\n\n";
				} else if("decimal".equals(dataType)) {
					importDecimal = "import java.math.BigDecimal;\n\n";
				}
			}
			if(StringUtils.isNotBlank(importDecimal)) {
				content.append(importDecimal);
			}
			if(StringUtils.isNotBlank(importDate)) {
				content.append(importDate);
			}
			content.append("import com.kakame.codenews.base.web.annotation.TableMapping;\n");
			content.append("import com.kakame.codenews.base.web.annotation.ColumnMapping;\n\n");
			// 类注释
			String tableComment = list.get(0).get(TABLE_COMMENT);
			content.append("/**").append("\n");
			content.append(" * ").append(tableComment).append("\n");
			content.append(" * ").append(AUTHOR_SIGN).append("\n");
			content.append(" */").append("\n");
			// 类声明
			content.append("@TableMapping(\"").append(tableName).append("\")\n");
			content.append("public class ").append(classFileName).append(" implements Serializable {").append("\n\n");
			content.append("    private static final long serialVersionUID = 1L;\n\n");
			// 属性声明
			for (Map<String, String> map : list) {
				String dataType = map.get(DATA_TYPE);
				String columnName = map.get(COLUMN_NAME);
				content.append("    @ColumnMapping(column = \"").append(columnName).append("\")\n");
				if("varchar".equals(dataType) || "text".equals(dataType)) {
					content.append("    private String ").append(toJavaPropertyName(columnName)).append(";\n\n");
				} else if("int".equals(dataType)) {
					content.append("    private Integer ").append(toJavaPropertyName(columnName)).append(";\n\n");
				} else if("datetime".equals(dataType) || "date".equals(dataType)) {
					content.append("    private Date ").append(toJavaPropertyName(columnName)).append(";\n\n");
				} else if("decimal".equals(dataType)) {
					content.append("    private BigDecimal ").append(toJavaPropertyName(columnName)).append(";\n\n");
				}
			}
			content.append("\n");
			// 构造方法
			content.append("    public ").append(classFileName).append("() {\n");
			content.append("    }\n");
			content.append("    public ").append(classFileName).append("(Builder builder) {\n");
			for (Map<String, String> map : list) {
				String columnName = map.get(COLUMN_NAME);
				String javaPropertyName = toJavaPropertyName(columnName);
				content.append("        this.").append(javaPropertyName).append(" = builder.").append(javaPropertyName).append(";\n");
			}
			content.append("    }\n");
			content.append("	public static Builder create() {\n");
			content.append("		return new Builder();\n");
			content.append("	}\n");
			content.append("\n");
			// get set方法声明
			for (Map<String, String> map : list) {
				String dataType = map.get(DATA_TYPE);
				String columnName = map.get(COLUMN_NAME);
				String columnComment = map.get(COLUMN_COMMENT);
				
				String javaPropertyName = toJavaPropertyName(columnName);
				String methodName = toJavaClassName(columnName);
				
				content.append("	/**").append("\n");
				content.append("	 * ").append(columnComment).append("\n");
				content.append("	 * ").append(AUTHOR_SIGN).append("\n");
				content.append("	 * @return").append("\n");
				content.append("	 */").append("\n");
				
				if("varchar".equals(dataType) || "text".equals(dataType)) {
					dataType = "String";
				} else if("int".equals(dataType)) {
					dataType = "Integer";
				} else if("datetime".equals(dataType) || "date".equals(dataType)) {
					dataType = "Date";
				} else if("decimal".equals(dataType)) {
					dataType = "BigDecimal";
				}
				content.append("    public ").append(dataType).append(" get").append(methodName).append("() {").append("\n");
				content.append("		return ").append(javaPropertyName).append(";\n");
				content.append("	}").append("\n");
				content.append("    /**").append("\n");
				content.append("	 * ").append(columnComment).append("\n");
				content.append("	 * ").append(AUTHOR_SIGN).append("\n");
				content.append("	 * @param ").append(javaPropertyName).append("\n");
				content.append("	 */").append("\n");
				content.append("    public void set").append(methodName).append("(").append(dataType).append(" ").append(javaPropertyName).append(") {").append("\n");
				content.append("		this.").append(javaPropertyName).append(" = ").append(javaPropertyName).append(";\n");
				content.append("	}").append("\n");
			}
			content.append("\n");
			// toString 声明
			content.append("    @Override").append("\n");
			content.append("	public String toString() {").append("\n");
			content.append("		return \"").append(classFileName).append(" [");
			int loop = 0;
			for (Map<String, String> map : list) {
				String columnName = map.get(COLUMN_NAME);
				String javaPropertyName = toJavaPropertyName(columnName);
				content.append(javaPropertyName).append("=\" + ").append(javaPropertyName);
				if(loop < list.size() - 1) {
					content.append(" + \", ");
				}
				loop ++;
			}
			content.append(" + \"]\";").append("\n");
			content.append("	}").append("\n");
			// 构建者
			// 类声明
			content.append("    public static class Builder {").append("\n");
			// 属性声明
			for (Map<String, String> map : list) {
				String dataType = map.get(DATA_TYPE);
				String columnName = map.get(COLUMN_NAME);
				if("varchar".equals(dataType) || "text".equals(dataType)) {
					content.append("        private String ").append(toJavaPropertyName(columnName)).append(";\n");
				} else if("int".equals(dataType)) {
					content.append("        private Integer ").append(toJavaPropertyName(columnName)).append(";\n");
				} else if("datetime".equals(dataType) || "date".equals(dataType)) {
					content.append("        private Date ").append(toJavaPropertyName(columnName)).append(";\n");
				} else if("decimal".equals(dataType)) {
					content.append("        private BigDecimal ").append(toJavaPropertyName(columnName)).append(";\n");
				}
			}
			// set方法声明
			for (Map<String, String> map : list) {
				String dataType = map.get(DATA_TYPE);
				String columnName = map.get(COLUMN_NAME);
				String columnComment = map.get(COLUMN_COMMENT);
				
				String javaPropertyName = toJavaPropertyName(columnName);
//				String methodName = toJavaClassName(columnName);
				if("varchar".equals(dataType) || "text".equals(dataType)) {
					dataType = "String";
				} else if("int".equals(dataType)) {
					dataType = "Integer";
				} else if("datetime".equals(dataType) || "date".equals(dataType)) {
					dataType = "Date";
				} else if("decimal".equals(dataType)) {
					dataType = "BigDecimal";
				}
				content.append("        /**").append("\n");
				content.append("	     * ").append(columnComment).append("\n");
				content.append("	     * ").append(AUTHOR_SIGN).append("\n");
				content.append("	     * @param ").append(javaPropertyName).append("\n");
				content.append("	     */").append("\n");
				content.append("        public Builder ").append(javaPropertyName).append("(").append(dataType).append(" ").append(javaPropertyName).append(") {").append("\n");
				content.append("	    	this.").append(javaPropertyName).append(" = ").append(javaPropertyName).append(";\n");
				content.append("	        return this;\n");
				content.append("    	}").append("\n");
			}
			content.append("\n");
			content.append("        public ").append(classFileName).append(" build() {\n");
			content.append("			return new ").append(classFileName).append("(this);\n");
			content.append("		}\n");
			content.append("	}\n\n\n\n");
			content.append("    // ******************* 扩展 *******************//\n\n\n\n\n");
			content.append("}").append("\n");
			pw.print(content.toString());
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if(pw != null) {
				pw.flush();
				pw.close();
			}
		}
		
	}

	/**
	 * 将helloWorld转换成hello_world
	 * @author 何家明 2018年6月30日 下午7:08:10
	 * @param fieldName
	 * @return
	 */
	public static String toDbName(String fieldName) {
        if (fieldName == null) {
            return "";
        }
        char[] chars = fieldName.toCharArray();
        StringBuilder sb = new StringBuilder();
        for (char c : chars) {
            if (CharUtils.isAsciiAlphaUpper(c)) {
                sb.append("_" + (char) (c + 32));
            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }
	/**
	 * java属性名转换，将hello_world转换成helloWorld
	 * @author 何家明 2018年6月30日 下午7:08:10
	 * @param fieldName
	 * @return
	 */
	public static String toJavaPropertyName(String fieldName) {
        if (fieldName == null) {
            return "";
        }
        char[] chars = fieldName.toCharArray();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < chars.length; i++) {
            if (chars[i] == '_') {
            	if(i == chars.length - 1) {
            		break;
            	}
            	if(chars[i + 1] == '_') {
            		continue;
            	}
            	sb.append((char)(chars[++i] - 32));
            } else {
                sb.append(chars[i]);
            }
        }
        return sb.toString();
    }
	
	/**
	 * java类名转换，将hello_world转换成HelloWorld
	 * @author 何家明 2018年6月30日 下午7:08:10
	 * @param fieldName
	 * @return
	 */
	private static String toJavaClassName(String fieldName) {
		String javaPropertyName = toJavaPropertyName(fieldName);
		if(StringUtils.isNotBlank(javaPropertyName)) {
			char[] ch = javaPropertyName.toCharArray();
		    if (ch[0] >= 'a' && ch[0] <= 'z') {
		        ch[0] = (char) (ch[0] - 32);
		    }
		    return new String(ch);
		}
		return "";
    }
	/**
	 * 读取数据库表信息
	 * @author 何家明 2018年6月30日 下午4:04:14
	 * @return
	 */
	public Map<String, List<Map<String, String>>> readAllTableAndColumns() {
		Map<String, List<Map<String, String>>> resultMap = new HashMap<String, List<Map<String, String>>>();
		ResultSet rs = null;
		try {
			String sql = "SELECT col.TABLE_NAME, col.COLUMN_NAME, col.DATA_TYPE, col.COLUMN_COMMENT, tab.TABLE_COMMENT "
		               + "  FROM INFORMATION_SCHEMA.COLUMNS col "
					   + "  LEFT JOIN information_schema.TABLES tab ON col.TABLE_NAME = tab.TABLE_NAME "
		               + "  WHERE col.TABLE_SCHEMA = 'codenews' and tab.TABLE_SCHEMA = 'codenews'";
			rs = this.statement.executeQuery(sql);
			while(rs.next()) {
				String tableName = rs.getString(1);
				String columnName = rs.getString(2);
				String dataType = rs.getString(3);
				String columnComment = rs.getString(4);
				String tableComment = rs.getString(5);
				
				Map<String, String> columnMap = new HashMap<String, String>();
				columnMap.put(COLUMN_NAME, columnName);
				columnMap.put(DATA_TYPE, dataType);
				columnMap.put(COLUMN_COMMENT, columnComment);
				columnMap.put(TABLE_COMMENT, tableComment);
				
				List<Map<String, String>> list = resultMap.get(tableName);
				if(list == null) {
					List<Map<String, String>> columnList = new ArrayList<Map<String, String>>();
					columnList.add(columnMap);
					resultMap.put(tableName, columnList);
				} else {
					list.add(columnMap);
					resultMap.put(tableName, list);
				}
			}
			return resultMap;
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			try {
				if(rs != null) {
					rs.close();
				}
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		return null;
	}
	
	public void initConnection(String driverClass, String dbUrl, String username, String password) throws Exception {
		Class.forName(driverClass);
		this.connection = DriverManager.getConnection(dbUrl, username, password);
		this.statement = this.connection.createStatement();
	}
	
	public void closeConnect() {
		try {
			if(this.statement != null) {
				this.statement.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
		try {
			if(this.connection != null) {
				this.connection.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
}
